import os
import re
import sys
import json
import time
import uuid
import platform
import logging
import argparse
import importlib
import subprocess
from typing import List, Dict, Any, Optional
from datetime import datetime
import traceback

# 导入项目中的其他模块
from ds_api import call_llm_api

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

class TestRecorder:
    """测试记录器基类，提供记录测试步骤、断言和警告的通用方法"""
    
    def __init__(self):
        self.test_steps = []
        self.assertions = []
        self.warnings = []
    
    def add_test_step(self, step_id, step_name, step_status, step_message, step_duration_ms):
        """添加测试步骤"""
        self.test_steps.append({
            "step_id": step_id,
            "step_name": step_name,
            "step_status": step_status,
            "step_message": step_message,
            "step_duration_ms": step_duration_ms
        })
    
    def add_assertion(self, assertion_id, assertion_description, assertion_status, expected_value, actual_value):
        """添加断言结果"""
        self.assertions.append({
            "assertion_id": assertion_id,
            "assertion_description": assertion_description,
            "assertion_status": assertion_status,
            "expected_value": expected_value,
            "actual_value": actual_value
        })
    
    def add_warning(self, warning_code, warning_message, warning_level="medium"):
        """添加警告信息"""
        self.warnings.append({
            "warning_code": warning_code,
            "warning_message": warning_message,
            "warning_level": warning_level
        })

class PyPITestResult(TestRecorder):
    """测试结果类，用于存储和格式化测试结果"""
    
    def __init__(self, package_name: str, test_name: str):
        super().__init__()
        self.package_name = package_name
        self.test_name = test_name
        self.status = "pending"  # pending, running, passed, failed, error
        self.start_time = None
        self.end_time = None
        self.duration = None
        self.error_message = None
        self.traceback = None
        self.output = None
        self.test_id = f"{package_name}-{uuid.uuid4().hex[:8]}"
        self.package_version = None
        self.os_info = self._get_os_info()
        self.environment_info = self._get_environment_info()
        self.root_cause = None
        self.recommendations = []
        
    def start(self):
        """开始测试"""
        self.status = "running"
        self.start_time = datetime.now()
        
    def success(self, output: Optional[str] = None):
        """测试成功"""
        self.status = "passed"
        self.end_time = datetime.now()
        self.output = output
        if self.start_time:
            self.duration = (self.end_time - self.start_time).total_seconds()
        
    def failure(self, error_message: str, tb: Optional[str] = None, output: Optional[str] = None):
        """测试失败"""
        self.status = "failed"
        self.end_time = datetime.now()
        self.error_message = error_message
        self.traceback = tb
        self.output = output
        if self.start_time:
            self.duration = (self.end_time - self.start_time).total_seconds()
    
    def error(self, error_message: str, tb: Optional[str] = None):
        """测试错误"""
        self.status = "error"
        self.end_time = datetime.now()
        self.error_message = error_message
        self.traceback = tb
        if self.start_time:
            self.duration = (self.end_time - self.start_time).total_seconds()
    
    def _get_os_info(self):
        """获取操作系统信息"""
        import platform
        return {
            "name": platform.system(),
            "version": platform.version(),
            "arch": platform.machine()
        }
    
    def _get_environment_info(self):
        """获取环境信息"""
        import platform
        import sys
        import pkg_resources
        
        # 获取系统资源信息
        system_resources = {}
        try:
            import psutil
            system_resources = {
                "cpu_info": f"{psutil.cpu_count()} cores",
                "memory_info": f"{psutil.virtual_memory().total / (1024**3):.2f} GB"
            }
        except ImportError:
            system_resources = {
                "cpu_info": "Unknown",
                "memory_info": "Unknown"
            }
        
        return {
            "python_version": platform.python_version(),
            "system_resources": system_resources
        }
    
    def to_dict(self) -> Dict[str, Any]:
        """将测试结果转换为标准化的JSON格式字典"""
        # 将状态映射到标准格式
        status_map = {
            "passed": "success",
            "failed": "failure",
            "error": "failure",
            "pending": "warning",
            "running": "warning"
        }
        
        # 计算持续时间（毫秒）
        duration_ms = None
        if self.duration is not None:
            duration_ms = int(self.duration * 1000)
        
        # 构建错误信息
        error_info = None
        if self.error_message or self.traceback:
            error_info = {
                "error_code": "TEST_FAILURE",
                "error_type": "TestFailure",
                "error_message": self.error_message or "Unknown error",
                "stack_trace": self.traceback
            }
        
        # 构建测试详情
        test_details = {
            "test_case_name": self.test_name,
            "test_case_description": f"基本功能测试 {self.package_name}",
            "test_steps": self.test_steps,
            "assertions": self.assertions
        }

        # 构建完整的测试结果
        return {
            "test_id": self.test_id,
            "package_name": self.package_name,
            "package_version": self.package_version,
            "test_timestamp": self.start_time.isoformat() if self.start_time else None,
            "test_duration_ms": duration_ms,
            "os_info": self.os_info,
            "test_status": status_map.get(self.status, "warning"),
            "test_summary": f"包 {self.package_name} 的基本功能测试{'通过' if self.status == 'passed' else '失败'}",
            "test_details": test_details,
            "error_info": error_info,
            "warning_info": self.warnings,
            "root_cause": self.root_cause,
            "environment_info": self.environment_info,
            "metadata": {
                "generated_by": "AutoVerifier-BaseTester",
                "test_category": "基础功能测试",
                "tags": ["自动化", "基本功能", "PyPI"]
            }
        }

class PyPIBaseTester(TestRecorder):
    def __init__(self, package_name: str):
        super().__init__()
        self.package_name = package_name
        self.package_version = "unknown"
        self.test_id = str(uuid.uuid4())
        self.error_info = {}
        self.success = True
        self.start_time = time.time()
        self.module_dir_list = []
        self.python_version = sys.version
        self.root_cause = None
        self.recommendations = None

    def _get_package_version(self, module):
        """尝试获取包的版本信息"""
        version_attrs = ['__version__', 'VERSION', 'version', 'version_info']
        for attr in version_attrs:
            if hasattr(module, attr):
                try:
                    version = getattr(module, attr)
                    if callable(version):
                        version = version()
                    return str(version)
                except Exception:
                    continue
        
        # 如果常见属性都没有找到，尝试使用importlib.metadata (Python 3.8+)
        try:
            if sys.version_info >= (3, 8):
                from importlib import metadata
                return metadata.version(self.package_name)
        except Exception:
            pass
            
        return "unknown"
    
    def _record_step(self, step_id, step_name, step_status, step_message, step_duration_ms):
        """记录测试步骤信息"""
        self.add_test_step(step_id, step_name, step_status, step_message, step_duration_ms)

    def _record_assertion(self, assertion_id, assertion_description, assertion_status, 
                         expected_value, actual_value):
        """记录断言结果"""
        self.add_assertion(assertion_id, assertion_description, assertion_status, 
                          expected_value, actual_value)

    def _record_warning(self, warning_code, warning_message, warning_level="medium"):
        """记录警告信息"""
        # 直接使用基类的add_warning方法
        self.add_warning(warning_code, warning_message, warning_level)

    def _record_error(self, error_type, error_message, error_code="TEST_ERROR"):
        """记录错误信息"""
        self.error_info = {
            "error_code": error_code,
            "error_type": error_type,
            "error_message": error_message,
            "stack_trace": traceback.format_exc()
        }
        self.success = False

    def test_basic(self):
        """测试包是否可以导入"""
        step_start = time.time()
        step_id = 1
        step_name = "包导入测试"
        
        try:
            module = importlib.import_module(self.package_name)
            step_duration = int((time.time() - step_start) * 1000)
            
            if module is not None:
                self.package_version = self._get_package_version(module)
                self._record_step(step_id, step_name, "success", 
                                 f"成功导入包 {self.package_name}", step_duration)
                self._record_assertion(1, "检查包是否能被导入", "success", 
                                      "模块对象不为None", str(type(module)))
                
                # 记录版本获取结果
                if self.package_version != "unknown":
                    self._record_assertion(2, "检查包版本信息", "success", 
                                          "版本字符串", self.package_version)
                else:
                    self._record_assertion(2, "检查包版本信息", "warning", 
                                          "版本字符串", "unknown")
                    self._record_warning("VERSION_WARNING", "无法自动检测包版本", "low")
                
                # 记录收集到的模块信息
                if self.module_dir_list:
                    self._record_assertion(3, "检查模块属性收集", "success", 
                                          "收集到模块属性", f"共{len(self.module_dir_list)}个属性")
            else:
                self._record_step(step_id, step_name, "failure", 
                                 f"无法导入包 {self.package_name}", step_duration)
                self._record_assertion(1, "检查包是否能被导入", "failure", 
                                      "模块对象不为None", "None")
                self._record_error("ImportError", f"无法导入包 {self.package_name}")
                
        except Exception as e:
            step_duration = int((time.time() - step_start) * 1000)
            self._record_step(step_id, step_name, "failure", 
                             f"导入包 {self.package_name} 时出错: {str(e)}", step_duration)
            self._record_assertion(1, "检查包是否能被导入", "failure", 
                                  "成功导入", f"错误: {str(e)}")
            self._record_error(type(e).__name__, str(e))

    def test_core(self):
        """测试包是否有可用属性"""
        if not self.success:  # 如果之前的测试已失败，跳过此测试
            return
            
        step_start = time.time()
        step_id = 2
        step_name = "包核心功能测试"
        
        try:
            module = importlib.import_module(self.package_name)
            attrs = dir(module)
            step_duration = int((time.time() - step_start) * 1000)
            
            if len(attrs) > 0:
                self._record_step(step_id, step_name, "success", 
                                 f"包 {self.package_name} 包含 {len(attrs)} 个属性", step_duration)
                self._record_assertion(1, "检查模块属性数量 > 0", "success", 
                                      "> 0", str(len(attrs)))
                
                # 检查是否有常用方法/属性
                common_methods = ['__version__', '__name__', '__file__']
                available_methods = [m for m in common_methods if hasattr(module, m)]
                
                if available_methods:
                    self._record_assertion(2, "检查常用属性存在", "success", 
                                          "至少一个常用属性存在", f"找到: {', '.join(available_methods)}")
                else:
                    self._record_warning("NO_COMMON_METHODS", "未找到常用属性", "low")
            else:
                self._record_step(step_id, step_name, "failure", 
                                 f"包 {self.package_name} 没有可用属性", step_duration)
                self._record_assertion(1, "检查模块属性数量 > 0", "failure", 
                                      "> 0", "0")
                self._record_error("AttributeError", f"包 {self.package_name} 没有可用属性")
                
        except Exception as e:
            step_duration = int((time.time() - step_start) * 1000)
            self._record_step(step_id, step_name, "failure", 
                             f"测试包 {self.package_name} 核心功能时出错: {str(e)}", step_duration)
            self._record_error(type(e).__name__, str(e))

    def test_edge(self):
        """测试异常处理"""
        if not self.success:  # 如果之前的测试已失败，跳过此测试
            return
            
        step_start = time.time()
        step_id = 3
        step_name = "异常处理测试"
        
        try:
            module = importlib.import_module(self.package_name)
            
            # 尝试访问不存在的属性
            try:
                getattr(module, "this_function_should_not_exist_123")
                step_duration = int((time.time() - step_start) * 1000)
                self._record_step(step_id, step_name, "failure", 
                                 "访问不存在的属性时未抛出异常", step_duration)
                self._record_assertion(1, "访问不存在的属性应抛出异常", "failure", 
                                      "AttributeError", "未抛出异常")
                self._record_error("AssertionError", "访问不存在的属性时未抛出预期异常")
            except AttributeError:
                step_duration = int((time.time() - step_start) * 1000)
                self._record_step(step_id, step_name, "success", 
                                 "访问不存在的属性时正确抛出AttributeError", step_duration)
                self._record_assertion(1, "访问不存在的属性应抛出异常", "success", 
                                      "AttributeError", "AttributeError")
            except Exception as e:
                step_duration = int((time.time() - step_start) * 1000)
                self._record_step(step_id, step_name, "warning", 
                                 f"访问不存在的属性时抛出非预期异常: {type(e).__name__}", step_duration)
                self._record_assertion(1, "访问不存在的属性应抛出异常", "warning", 
                                      "AttributeError", type(e).__name__)
                self._record_warning("UNEXPECTED_EXCEPTION", 
                                   f"访问不存在的属性时抛出非预期异常: {type(e).__name__}", "low")
                
        except Exception as e:
            step_duration = int((time.time() - step_start) * 1000)
            self._record_step(step_id, step_name, "failure", 
                             f"测试包 {self.package_name} 异常处理时出错: {str(e)}", step_duration)
            self._record_error(type(e).__name__, str(e))

    def run_all_tests(self):
        """执行所有测试并返回 JSON 结果"""
        # 执行测试
        self.test_basic()
        self.test_core()
        self.test_edge()
        
        # 计算总持续时间
        total_duration = int((time.time() - self.start_time) * 1000)
        
        # 确定测试状态
        if not self.success:
            test_status = "failure"
            test_summary = f"包 {self.package_name} 测试失败"
        elif self.warnings:
            test_status = "warning"
            test_summary = f"包 {self.package_name} 测试通过但有警告"
        else:
            test_status = "success"
            test_summary = f"包 {self.package_name} 测试通过"
        
        # 构建最终结果
        result = {
            "test_id": self.test_id,
            "package_name": self.package_name,
            "package_version": self.package_version,
            "test_timestamp": datetime.now().isoformat(),
            "test_duration_ms": total_duration,
            "os_info": {
                "name": platform.system(),
                "version": platform.release(),
                "arch": platform.machine()
            },
            "test_status": test_status,
            "test_summary": test_summary,
            "test_details": {
                "test_case_name": f"test_{self.package_name}",
                "test_case_description": f"测试 {self.package_name} 包的基本功能",
                "test_steps": self.test_steps,
                "assertions": self.assertions
            },
            "error_info": self.error_info,
            "warning_info": self.warnings,
            "root_cause": self.root_cause,
            "environment_info": {
                "python_version": sys.version,
                "system_resources": {
                    "cpu_info": platform.processor(),
                    "memory_info": "N/A",  # 不使用外部包，无法获取内存信息
                    "gpu_info": "N/A"      # 不使用外部包，无法获取GPU信息
                }
            },
            "metadata": {
                "generated_by": "AutoVerifier-BaseTester",
                "test_category": "基础功能测试",
                "tags": ["导入测试", "基础功能", "兼容性"]
            }
        }
        
        return result

class PyPIIntelligentDiagnoser:
    """智能诊断器，使用LLM分析包测试失败原因并提供修复建议"""
    
    # 添加频率控制相关的类变量
    _last_llm_call_time = 0
    _qpm_interval = 60.0  # 默认每分钟1次请求的间隔
    _request_count = 0  # 请求计数器
    _max_requests_per_minute = 1  # 默认每分钟最多1次请求
    
    def __init__(
        self, 
        use_llm: bool = False, 
        model_name: str = "deepseek-reasoner",
        base_url: str = "https://api.deepseek.com",
        qpm: Optional[int] = 1
    ):
        self.use_llm = use_llm
        self.model_name = model_name
        self.base_url =base_url
        self.qpm = qpm or 1
        self._qpm_interval = 60.0 / self.qpm  # 计算每次请求的最小间隔
        self._max_requests_per_minute = self.qpm
    
    def _rate_limit(self):
        """实施请求频率限制"""
        current_time = time.time()
        elapsed = current_time - self._last_llm_call_time
        
        # 如果距离上次请求时间不足间隔时间，则等待
        if elapsed < self._qpm_interval:
            wait_time = self._qpm_interval - elapsed
            logger.info(f"频率限制: 等待 {wait_time:.2f} 秒后再请求")
            time.sleep(wait_time)
        
        # 检查每分钟请求次数限制
        if self._request_count >= self._max_requests_per_minute:
            # 如果已经达到每分钟最大请求数，等待到下一分钟
            next_minute = (int(current_time) // 60 + 1) * 60
            wait_time = next_minute - current_time
            if wait_time > 0:
                logger.info(f"已达到每分钟请求限制，等待 {wait_time:.2f} 秒")
                time.sleep(wait_time)
            self._request_count = 0
        
        # 更新最后请求时间和计数器
        self._last_llm_call_time = time.time()
        self._request_count += 1

    def analyze_failure(self, 
        package_name: str,
        error_message: str,
        traceback_str: str,
        python_version: Optional[str] = None,
        environment_info: Optional[Dict[str, Any]] = None
    ) -> Dict[str, Any]:
        """使用 LLM 分析失败原因并提供修复建议
        
        Args:
            package_name: 包名
            error_message: 错误信息
            traceback_str: 错误堆栈信息
            python_version: Python版本信息 (可选，如未提供则自动获取)
            environment_info: 环境信息 (可选，如未提供则自动获取)
            
        Returns:
            Dict 包含 
            {
              "root_cause": 根本原因, 
              "recommendations": 推荐的解决方案, 
              "analyzed_by": 分析者,
              "confidence_level": 置信度
            }
        """
        if not self.use_llm:
            logger.info("未启用 LLM 分析，返回默认结果")
            return {
                "root_cause": error_message or "未知错误",
                "recommendations": ["请检查包的安装和依赖"],
                "analyzed_by": "Heuristic",
                "confidence_level": "low"
            }
        
        logger.info(f"调用 LLM 智能诊断 {package_name} 的失败原因")
        
        # 自动获取未提供的环境信息
        if python_version is None:
            python_version = sys.version
        
        if environment_info is None:
            # 获取系统资源信息
            system_resources = {}
            try:
                import psutil
                system_resources = {
                    "cpu_info": f"{psutil.cpu_count()} cores",
                    "memory_info": f"{psutil.virtual_memory().total / (1024**3):.2f} GB"
                }
            except ImportError:
                system_resources = {
                    "cpu_info": "Unknown",
                    "memory_info": "Unknown"
                }
            
            environment_info = {
                "python_version": platform.python_version(),
                "system_resources": system_resources,
                "os_info": {
                    "name": platform.system(),
                    "version": platform.release(),
                    "arch": platform.machine()
                }
            }
        
        # 构建增强的提示词
        prompt = f"""
                  System:你是一名资深 Python 包诊断专家，精通软件包安装、依赖冲突、运行时错误和环境配置问题的诊断与修复。
                  你的任务是基于输入的错误与环境信息，分析问题并给出标准化 JSON 格式的诊断结果。

                  User:请分析以下 Python 包的安装或运行失败信息，并输出标准化诊断结果。

                  === 包信息 ===
                  包名: {package_name}
                  Python版本: {python_version}
                  
                  === 错误信息 ===
                  错误消息: {error_message}
                  错误堆栈:
                  {traceback_str}
                  
                  === 环境信息 ===
                  系统环境: {json.dumps(environment_info or {}, ensure_ascii=False)}
                  
                  === 诊断要求 ===
                  请基于以上信息进行智能诊断，输出标准JSON格式：
                  
                  输出示例：
                  {{
                    "root_cause": "失败的根本原因（简洁明了）",
                    "recommendations": [
                      "具体可执行的修复建议1",
                      "具体可执行的修复建议2",
                      "具体可执行的修复建议3"
                    ],
                    "confidence_level": "high|medium|low"
                  }}
                  
                  任务要求：
                  1. 只输出JSON，禁止任何额外解释或无关文字。
                  2. "root_cause"：简洁明了，直指失败的根本原因。
                  3. "recommendations"：必须给出 具体可执行的修复建议（如命令、配置修改、依赖安装）。
                  4. "confidence_level"：仅能从 "high" | "medium" | "low" 中选择，依据诊断确定性。
                  """
        
        try:
            # 实施频率限制
            self._rate_limit()
            # 使用API调用
            response = call_llm_api(
                prompt=prompt,
                model=self.model_name,
                base_url=self.base_url,
                temperature=0.7,
                max_tokens=2000,
                verbose=False
            )
            
            # 尝试解析 JSON
            parsed = None
            try:
                parsed = json.loads(response.strip())
            except Exception:
                logger.warning("LLM 输出非标准 JSON，尝试手动提取")
                match = re.search(r"\{.*\}", response, re.S)
                try:
                    parsed = json.loads(match.group(0))
                except Exception as e:
                    logger.error(f"正则提取 JSON 解析失败: {e}")

            if parsed:
                parsed["analyzed_by"] = "LLMDiagnoser"
                # 确保包含置信度字段
                if "confidence_level" not in parsed:
                    parsed["confidence_level"] = "medium"
                return parsed

            logger.warning("LLM 诊断失败，使用回退结果")
            return {
                "root_cause": error_message or "未知错误",
                "recommendations": ["请人工检查错误信息和依赖关系"],
                "analyzed_by": "LLMDiagnoser",
                "confidence_level": "low"
            }
                
        except Exception as e:
            logger.error(f"LLM 调用失败: {e}")
            return {
                "root_cause": error_message or "未知错误",
                "recommendations": ["请检查网络连接和LLM服务状态"],
                "analyzed_by": "Heuristic",
                "confidence_level": "low"
            }

class PyPITestRunner:
    """测试运行器，执行测试用例并收集结果"""
    
    def __init__(self, output_dir: str = "./test_results"):
        self.output_dir = output_dir
        
        # 创建输出目录
        if not os.path.exists(self.output_dir):
            os.makedirs(self.output_dir)
            logger.info(f"创建测试结果目录: {self.output_dir}")
    
    def run_basic_test(self, package_name: str) -> PyPITestResult:
        """使用基础测试器运行测试
        
        Args:
            package_name: 包名
            
        Returns:
            PyPITestResult: 测试结果
        """
        logger.info(f"使用基础测试器测试包: {package_name}")
        
        # 创建测试结果对象
        test_name = f"test_{package_name.replace('-', '_').replace('.', '_')}"
        result = PyPITestResult(package_name, test_name)
        result.start()
        
        try:
            # 使用基础测试器
            tester = PyPIBaseTester(package_name)
            test_result = tester.run_all_tests()
            
            # 更新结果
            result.package_version = test_result.get("package_version", "unknown")
            
            # 设置测试状态
            if test_result.get("test_status") == "success":
                result.success(output=json.dumps(test_result))
            else:
                error_info = test_result.get("error_info", {})
                error_message = error_info.get("error_message", "基础测试失败")
                result.failure(error_message, output=json.dumps(test_result))
                
            # 更新测试步骤和断言
            if "test_details" in test_result:
                result.test_steps = test_result["test_details"].get("test_steps", [])
                result.assertions = test_result["test_details"].get("assertions", [])
                
            # 更新警告信息
            result.warnings = test_result.get("warning_info", [])
            
        except Exception as e:
            tb = traceback.format_exc()
            result.error(str(e), tb)
        
        # 保存测试结果
        self._save_result(result)
        
        return result

    def _save_result(self, result: PyPITestResult):
        """保存测试结果"""
        result_file = os.path.join(self.output_dir, f"{result.package_name}_result.json")
        with open(result_file, 'w', encoding='utf-8') as f:
            json.dump(result.to_dict(), f, ensure_ascii=False, indent=2)
        
        logger.info(f"测试结果已保存: {result_file}")

class AutoVerifier:
    """自动化验证工具，集成安装、测试用例生成和测试执行"""
    
    def __init__(self, 
                 output_dir: str = "./auto_verify_results",
                 use_llm: bool = True,
                 model_name: str = "deepseek-reasoner",
                 base_url: str = "https://api.deepseek.com",
                 qpm: int = 1,
                 cache_days: int = 30
    ):
        self.output_dir = output_dir
        self.use_llm = use_llm
        self.model_name = model_name
        self.base_url = base_url
        self.qpm = qpm
        self.cache_days = cache_days
        
        # 创建输出目录
        if not os.path.exists(self.output_dir):
            os.makedirs(self.output_dir)
            logger.info(f"创建输出目录: {self.output_dir}")
        
        # 初始化缓存文件路径
        self.cache_file = os.path.join(self.output_dir, "verification_results.json")
        self.verification_cache = self._load_cache()

        # 创建测试结果目录
        self.test_results_dir = os.path.join(self.output_dir, "test_results")
        if not os.path.exists(self.test_results_dir):
            os.makedirs(self.test_results_dir)
        
        # 初始化组件
        self.intelligent_diagnoser = PyPIIntelligentDiagnoser(use_llm=use_llm, model_name=model_name, base_url=base_url ,qpm=qpm)
        self.test_runner = PyPITestRunner(output_dir=self.test_results_dir)
        
        # 结果记录
        self.results: Dict[str, Dict[str, Any]] = {}
    
    def _load_cache(self) -> Dict[str, Any]:
        """加载验证缓存"""
        if os.path.exists(self.cache_file):
            try:
                with open(self.cache_file, 'r', encoding='utf-8') as f:
                    cache = json.load(f)
                    logger.info(f"加载验证缓存，包含 {len(cache)} 个包")
                    return cache
            except Exception as e:
                logger.error(f"加载验证缓存失败: {e}")
                return {}
        return {}
    
    def _save_cache(self):
        """保存验证缓存"""
        try:
            with open(self.cache_file, 'w', encoding='utf-8') as f:
                json.dump(self.verification_cache, f, ensure_ascii=False, indent=2)
        except Exception as e:
            logger.error(f"保存验证缓存失败: {e}")
    
    def _is_cached_valid(self, package_name: str) -> bool:
        """检查缓存是否有效"""
        if package_name not in self.verification_cache:
            return False
        
        cached_data = self.verification_cache[package_name]
        
        # 检查缓存是否过期
        if self.cache_days > 0:
            cached_time = datetime.fromisoformat(cached_data['timestamp'])
            current_time = datetime.now()
            days_diff = (current_time - cached_time).days
            
            if days_diff > self.cache_days:
                logger.info(f"包 {package_name} 的缓存已过期 ({days_diff} 天前)")
                return False
        
        # 检查缓存状态是否为成功
        return cached_data.get('overall_status') == 'success'

    def verify_package(self, package_name: str) -> Dict[str, Any]:
        """验证单个包
        
        Args:
            package_name: 包名
            
        Returns:
            Dict: 验证结果
        """
        logger.info(f"\n=== 开始验证包: {package_name} ===")
        
        # 检查缓存
        if self._is_cached_valid(package_name):
            cached_result = self.verification_cache[package_name]
            logger.info(f"包 {package_name} 已有有效缓存，跳过验证")
            self.results[package_name] = cached_result
            return cached_result

        result = {
            "package_name": package_name,
            "timestamp": datetime.now().isoformat(),
            "test_execution": {
                "status": "pending",
                "result": None
            },
            "intelligent_diagnosis": None,
            "overall_status": "pending"
        }
        
        # 步骤1: 使用BaseTester执行基础测试
        logger.info(f"步骤1: 使用BaseTester测试 {package_name}")
        try:
            test_result = self.test_runner.run_basic_test(package_name)
            result["test_execution"]["status"] = "completed"
            result["test_execution"]["result"] = test_result.to_dict()
    
            if test_result.status == "passed":
                result["overall_status"] = "success"
                logger.info(f"✓ {package_name} 基础测试通过")
            else:
                result["overall_status"] = "failed"
                logger.error(f"✗ {package_name} 基础测试失败: {test_result.error_message}")
    
                # 步骤2: 仅在测试失败且启用诊断功能时调用智能诊断器
                if self.use_llm:
                    logger.info(f"步骤2: 调用智能诊断器分析 {package_name} 的失败原因")
                    
                    # 直接调用智能诊断器，让其自动获取环境信息
                    failure_analysis = self.intelligent_diagnoser.analyze_failure(
                        package_name=package_name,
                        error_message=test_result.error_message,
                        traceback_str=test_result.traceback
                    )
                    
                    # 更新测试结果中的根本原因
                    test_result.root_cause = failure_analysis.get("root_cause")
                    
                    # 更新结果字典
                    result["intelligent_diagnosis"] = failure_analysis
                    result["test_execution"]["result"] = test_result.to_dict()  # 更新包含诊断信息的测试结果
                    
                    logger.info(f"✓ 智能诊断完成，置信度: {failure_analysis.get('confidence_level', 'unknown')}")
                else:
                    logger.info(f"智能诊断功能已禁用，跳过分析步骤")
    
        except Exception as e:
            result["test_execution"]["status"] = "error"
            result["test_execution"]["error"] = str(e)
            result["overall_status"] = "failed"
            logger.error(f"✗ {package_name} 测试执行出错: {str(e)}")

        # 更新缓存（只缓存成功的验证）
        if result['overall_status'] == 'success':
            self.verification_cache[package_name] = result
            self._save_cache()

        # 保存 JSON 结果
        self.results[package_name] = result
        self._save_results()
        return result
    
    def verify_packages(self, package_names: List[str]) -> Dict[str, Dict[str, Any]]:
        """批量验证多个包
        
        Args:
            package_names: 包名列表
            
        Returns:
            Dict: 验证结果字典，包名 -> 结果
        """
        logger.info(f"开始批量验证 {len(package_names)} 个包")
        
        for package_name in package_names:
            self.verify_package(package_name)
        
        # 打印总结
        self._print_summary()
        
        return self.results
    
    def _save_results(self):
        """保存所有结果"""
        results_file = os.path.join(self.output_dir, "verification_results.json")
        with open(results_file, 'w', encoding='utf-8') as f:
            json.dump(self.results, f, ensure_ascii=False, indent=2)
        
        logger.info(f"验证结果已保存: {results_file}")
    
    def _print_summary(self):
        """打印验证总结"""
        if not self.results:
            logger.info("没有验证结果")
            return
        
        total = len(self.results)
        success = sum(1 for r in self.results.values() if r["overall_status"] == "success")
        failed = total - success
        
        logger.info(f"\n{'='*50}")
        logger.info("验证总结:")
        logger.info(f"总包数: {total}")
        logger.info(f"成功: {success} ({success/total*100:.1f}%)")
        logger.info(f"失败: {failed} ({failed/total*100:.1f}%)")
        
        # 仅在启用智能诊断时显示相关统计
        if self.use_llm:
            diagnosed_count = sum(1 for r in self.results.values() if r.get("intelligent_diagnosis") is not None)
            logger.info(f"使用智能诊断: {diagnosed_count} ({diagnosed_count/total*100:.1f}%)")
        
        if failed > 0:
            logger.info("\n失败的包:")
            for package_name, result in self.results.items():
                if result["overall_status"] != "success":
                    # 确定失败原因和诊断信息
                    if result["test_execution"]["status"] != "completed":
                        stage = "测试执行"
                        error = result["test_execution"].get("error", "未知错误")
                        diagnosis_info = ""
                    else:
                        stage = "基础测试"
                        test_result = result["test_execution"].get("result", {})
                        error = test_result.get("error_info", {}).get("error_message", "未知错误")
                        
                        # 检查是否有根本原因分析
                        root_cause = test_result.get("root_cause")
                        if root_cause:
                            error = root_cause
                        
                        # 仅在启用智能诊断时添加诊断信息
                        if self.use_llm:
                            diagnosis = result.get("intelligent_diagnosis")
                            if diagnosis:
                                diag_root_cause = diagnosis.get("root_cause", "未知原因")
                                confidence = diagnosis.get("confidence_level", "unknown")
                                diagnosis_info = f" [诊断: {diag_root_cause}, 置信度: {confidence}]"
                            else:
                                diagnosis_info = " [未进行智能诊断]"
                        else:
                            diagnosis_info = ""
                    
                    # 获取原始包名（如果有记录）
                    original_name = package_name
                    if "原名:" in result.get("package_info", ""):
                        original_name = result.get("package_info", "").split("原名:")[1].strip()
                    
                    logger.info(f"{original_name}: 在{stage}阶段失败 - {error}{diagnosis_info}")

def main():
    parser = argparse.ArgumentParser(description="OpenCloudOS 9 AI软件自动化验证工具")
    
    # 包安装参数
    parser.add_argument("--result-file", default='./2025实战任务_作品文件夹/OpenCloudOS 9 AI软件自动化验证工具/张耀翔_作品/pipeline_output/test_results.txt', help="包含包名的文件，每行一个包名")
    
    # 智能诊断参数
    parser.add_argument("--use-llm", action="store_true", help="是否启用智能诊断功能")
    parser.add_argument("--base-url", default="https://openrouter.ai/api/v1", help="智能诊断服务的URL")
    parser.add_argument("--model-name", default="deepseek/deepseek-chat-v3.1:free", help="使用的智能诊断模型名称")
    parser.add_argument("--qpm", default=4, type=int, help="LLM API请求的QPM限制")
    
    # 输出参数
    parser.add_argument("--output-dir", default="./2025实战任务_作品文件夹/OpenCloudOS 9 AI软件自动化验证工具/张耀翔_作品/pipeline_output/auto_verify_results", help="输出目录")
    
    # 缓存参数
    parser.add_argument("--cache-days", default=30, type=int, help="缓存有效期（天数）")

    args = parser.parse_args()
    
    # 创建验证器
    verifier = AutoVerifier(
        output_dir=args.output_dir,
        use_llm=not args.use_llm,
        model_name=args.model_name,
        qpm=args.qpm,
        cache_days=args.cache_days
    )
    
    # 获取仓库和包名列表
    if args.result_file and os.path.exists(args.result_file):
        logger.info(f"从result.txt文件读取包名: {args.result_file}")
        try:
            with open(args.result_file, 'r', encoding='utf-8') as f:
                for line_num, line in enumerate(f, 1):
                    line = line.strip()
                    if line and not line.startswith('#'):
                        # 使用正则表达式提取方括号内的内容
                        import re
                        match = re.search(r'\[(.*)\]', line)
                        
                        if match:
                            # 提取GitHub名称和URL
                            parts = line.split()
                            if len(parts) >= 2:
                                github_name = parts[0]
                                github_url = parts[1]
                                
                                # 提取包列表
                                package_list_str = match.group(1)
                                # 分割包名并去除空白
                                package_names = [pkg.strip() for pkg in package_list_str.split(',')]
                                
                                logger.info(f"处理仓库: {github_name}, GitHub URL: {github_url}")
                                logger.info(f"包含的PyPI包: {', '.join(package_names)}")
                                
                                # 逐个验证仓库中的每个包
                                for package_name in package_names:
                                    if package_name:  # 确保包名非空
                                        logger.info(f"开始验证仓库 {github_name} 中的包: {package_name}")
                                        verifier.verify_package(package_name)
                                    else:
                                        logger.warning(f"第{line_num}行发现空的包名，跳过")
                            else:
                                logger.warning(f"第{line_num}行格式不正确，缺少GitHub名称或URL: {line}")
                        else:
                            # 尝试提取GitHub名称
                            parts = line.split()
                            if parts:
                                github_name = parts[0]
                                logger.warning(f"仓库 {github_name} (第{line_num}行) 没有找到PyPI包列表，跳过验证")
                            else:
                                logger.warning(f"第{line_num}行无法解析: {line}")
        except Exception as e:
            logger.error(f"读取result.txt文件失败: {str(e)}")
            logger.error(traceback.format_exc())
        
        # 打印验证总结
        verifier._print_summary()

if __name__ == "__main__":
    main()